package vooga.core;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

import vooga.geom.DoubleDimension;

/**
 * Layer is an Image that can be drawn in the VoogaView as the display for
 * a game.  It supports abstract coordinate systems by providing a Graphics2D
 * object with the appropriate transforms.
 * 
 * It doesn't support flipping of coordinates yet... Y coordinate always increases as you go down on screen
 * 
 * @author Congyi Wu 
 */
public class Layer implements Comparable<Layer>
{
    public static final int DEFAULT_Z_ORDER = 0;
	public static final Color CLEAR_BACKGROUND_COLOR = new Color(255, 255, 255, 0);//clear background by default
	
	private Rectangle2D.Double myAbstractBounds;
	private Rectangle2D.Double myPhysicalPercentBounds; //actual position from top left and size of Layer relative to it's container as percentages (1 = 100%)
	private Rectangle2D.Double myAbstractCropBounds; //abstract bounds to crop the picture by
	private BufferedImage myImage;
	private int myZorder;
	private Color myBackGroundColor;
	
	/* Construct a layer positioned at physical coordinates (0,0) that fills entire view
	 * @param zOrder z-order of layer, any int, smaller ints at bottom
	 * @param abstractBounds user specified abstract bounds of Layer 
	 * @param physicalPercentSize actual size of Layer relative to it's container as percentages (1 = 100%) 
	 */
	public Layer(int zOrder, Rectangle2D abstractBounds)
	{
		
		this(zOrder, abstractBounds, new Rectangle2D.Double(0, 0, 1, 1));
	}
	
	/* Construct a layer positioned at physical coordinates (0,0)
	 * @param zOrder z-order of layer, any int, smaller ints at bottom
	 * @param abstractBounds user specified abstract bounds of Layer 
	 * @param physicalPercentSize actual size of Layer relative to it's container as percentages (1 = 100%) 
	 */
	public Layer(int zOrder, Rectangle2D abstractBounds, DoubleDimension physicalPercentSize)
	{
		
		this(zOrder, abstractBounds, new Rectangle2D.Double(0, 0, physicalPercentSize.getWidth(), physicalPercentSize.getHeight()));
	}
	
	/**
	 * 
	 * @param zOrder z-order of layer, any int, smaller ints at bottom
	 * @param abstractBounds user specified abstract bounds of Layer 
	 * @param physicalPercentBounds actual bounds of Layer relative to it's container as percentages
	 */
	public Layer(int zOrder, Rectangle2D abstractBounds, Rectangle2D.Double physicalPercentBounds)
	{
		this(zOrder, abstractBounds, physicalPercentBounds, abstractBounds);
	}

	/**
	 * 
	 * @param zOrder z-order of layer, any int, smaller ints at bottom
	 * @param abstractBounds user specified abstract bounds of Layer 
	 * @param physicalPercentBounds actual bounds of Layer relative to it's container as percentages
	 * @param abstractCropBounds the abstract coordinates to crop to
	 */
	public Layer(int zOrder, Rectangle2D abstractBounds, Rectangle2D.Double physicalPercentBounds, Rectangle2D abstractCropBounds)
	{
		myZorder = zOrder;
		myImage = null;
		myAbstractBounds = new Rectangle2D.Double();
		myAbstractBounds.setFrame(abstractBounds);
		myPhysicalPercentBounds = (Rectangle2D.Double)physicalPercentBounds.clone();
		myAbstractCropBounds = new Rectangle2D.Double();
		myAbstractCropBounds.setFrame(abstractCropBounds);
		myBackGroundColor = CLEAR_BACKGROUND_COLOR;
	}

	/**
	 * 
	 * @return a entire BufferedImage representing the Layer
	 */
	public BufferedImage getImage()
	{
		return myImage;
	}
	
	/**
	 * 
	 * @return a BufferedImage representing the viewable (cropped) portion of the Layer
	 */
	public BufferedImage getViewableImage()
	{
		if(myImage == null)
			return null;
		return myImage.getSubimage(
				(int)((myAbstractCropBounds.x - myAbstractBounds.x) * myImage.getWidth()),
				(int)((myAbstractCropBounds.y - myAbstractBounds.y) * myImage.getHeight()),
				(int)((myAbstractCropBounds.width / myAbstractBounds.width) * myImage.getWidth()),
				(int)((myAbstractCropBounds.height / myAbstractBounds.height) * myImage.getHeight()));
	}

	/**
	 * clear the entire layer to the background
	 */
	public void clear()
	{
		Graphics2D g = myImage.createGraphics();
		g.setBackground(myBackGroundColor);
		g.clearRect(0, 0, myImage.getWidth(), myImage.getHeight());
		g.dispose();
	}
	
	/**
	 * 
	 * @return the Graphics object for the Layer
	 */
	public Graphics2D getGraphics()
	{
		if(getImage() == null)
			return null;
		Graphics2D g = getImage().createGraphics();
		g.translate(myAbstractBounds.x, myAbstractBounds.y);
		g.scale(getImage().getWidth(null)/myAbstractBounds.getWidth(), getImage().getHeight(null)/myAbstractBounds.getHeight());
		g.setBackground(myBackGroundColor);
		return g;
	}

	public Rectangle2D getAbstractBounds()
	{
		return myAbstractBounds;
	}
	
	public void setAbstractBounds(Rectangle2D bounds)
	{
		myAbstractBounds.setFrame(bounds);
	}
	
	public double getAbstractTop()
	{
		return myAbstractBounds.x;
	}

	public double getAbstractLeft()
	{
		return myAbstractBounds.y;
	}

	public double getAbstractWidth()
	{
		return myAbstractBounds.width;
	}

	public double getAbstractHeight()
	{
		return myAbstractBounds.height;
	}
	
	public Rectangle2D getAbstractCropBounds()
	{
		return myAbstractCropBounds;
	}
	
	public void setAbstractCropBounds(Rectangle cropBounds)
	{
		myAbstractCropBounds.setFrame(cropBounds);
	}
	
/*  bad idea for now, mutating the Layer this way wil confuse the SortedMap in LayerSet
 * 	public void setZOrder(int zOrder)
	{
		myZorder = zOrder;
	}
*/	
	public int getZOrder()
	{
		return myZorder;
	}
	
	public Rectangle2D.Double getPhysicalPercentBounds()
	{
		return myPhysicalPercentBounds;
	}

	public double getPhysicalPercentFromTop()
	{
		return myPhysicalPercentBounds.y;
	}
	
	public double getPhysicalPercentFromLeft()
	{
		return myPhysicalPercentBounds.x;
	}

	public double getPhysicalPercentWidth()
	{
		return myPhysicalPercentBounds.width;
	}
	
	public double getPhysicalPercentHeight()
	{
		return myPhysicalPercentBounds.height;
	}
	
	/**
	 * updates the physical size of the Layer by 
	 *
	 */
	public void notifyContainerSizeChanged(Dimension d)
	{
		myImage = new BufferedImage(
				(int)(getPhysicalPercentWidth() * d.width),
				(int)(getPhysicalPercentHeight() * d.height),
				BufferedImage.TYPE_INT_ARGB);
	}

	public int compareTo(Layer l) {
		return ((Integer)getZOrder()).compareTo(l.getZOrder());
	}
	
	/**
	 * 
	 * @param z another z-order
	 * @return
	 */
	public int compareTo(int z) {
		return ((Integer)getZOrder()).compareTo(z);
	}

	public Color getBackGroundColor() {
		return myBackGroundColor;
	}

	public void setBackGroundColor(Color backGroundColor) {
		myBackGroundColor = backGroundColor;
	}
}
